﻿using AvalonDock.Layout.Serialization;
using Cinch;
using FR.SAFTAnalyser.Services;
using FR.SAFTAnalyser.ViewModels.Workspace;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Threading;

namespace FR.SAFTAnalyser
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region fields
        //private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        private Window mMainWin;
        private bool mOKToClose;

        public const string IssueTrackerTitle = "Unhandled Error";
        public const string IssueTrackerText = "Please click on the link below to check if this is a known problem.\n\n" +
                                               "Please report this problem if it is not known, yet.\n\n" +
                                               "The problem report should contain the error message below (you can use the copy button)\n" +
                                               "and a statement about the function/workflow you were using. Attach screenshots or sample files if applicable.";
        public const string IssueTrackerLink = "https://CodeSHARPer.codeplex.com/workitem/list/basic";
        #endregion fields

        #region constructor
        public App()
        {
            CinchBootStrapper.Initialise(new List<Assembly> { typeof(App).Assembly });
        }
        #endregion constructor

        #region properties
        /// <summary>
        /// Get a path to the directory where the application
        /// can persist/load user data on session exit and re-start.
        /// </summary>
        public static string DirAppData
        {
            get
            {
                return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), App.Company);
            }
        }

        /// <summary>
        /// Get a path to the directory where the user store his documents
        /// </summary>
        public static string MyDocumentsUserDir
        {
            get
            {
                return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            }
        }

        /// <summary>
        /// Convinience property to get the name of the executing assembly (usually name of *.exe file)
        /// </summary>
        internal static string AssemblyTitle
        {
            get
            {
                return Assembly.GetEntryAssembly().GetName().Name;
            }
        }

        internal static string Company
        {
            get
            {
                return "VisualSAFT";
            }
        }

        /// <summary>
        /// Get path and file name to application specific session file
        /// </summary>
        internal static string DirFileAppSessionData
        {
            get
            {
                return Path.Combine(App.DirAppData,
                    string.Format(CultureInfo.InvariantCulture, "{0}.session", App.AssemblyTitle));
            }
        }
        #endregion properties

        #region methods
        /// <summary>
        /// Create a dedicated directory to store program settings and session data
        /// </summary>
        /// <returns></returns>
        public static bool CreateAppDataFolder()
        {
            try
            {
                if (Directory.Exists(App.DirAppData) == false)
                    Directory.CreateDirectory(App.DirAppData);
            }
            catch
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Interpret command line parameters and process their content
        /// </summary>
        /// <param name="args"></param>
        private static void ProcessCmdLine(IEnumerable<string> args)
        {
            if (args != null)
                Workspace.Instance.OpenSaftFile(args.ElementAt(0));
        }

        /// <summary>
        /// Process command line args and window activation when switching from 1st to 2nd instance
        /// </summary>
        /// <param name="args"></param>
        public static void ProcessSecondInstance(IEnumerable<string> args)
        {
            var dispatcher = Current.Dispatcher;
            if (dispatcher.CheckAccess())
            {
                // The current application is the first
                // This case is already handled via start-up code in App.cs
                // ShowArgs(args);
            }
            else
            {
                dispatcher.BeginInvoke(
                  new Action(delegate
                  {
                      var mainWindow = Current.MainWindow as MainWindowView;

                      if (mainWindow != null)
                      {
                          if (mainWindow.IsVisible == false)
                              mainWindow.Show();

                          if (mainWindow.WindowState == WindowState.Minimized)
                              mainWindow.WindowState = WindowState.Normal;

                          mainWindow.Topmost = true;
                          mainWindow.Show();

                          Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.SystemIdle, (Action)delegate
                          {
                              mainWindow.Activate();
                              mainWindow.Topmost = false;
                          });
                      }

                      // Filter name of executeable if present in command line args
                      if (args != null)
                          ProcessCmdLine(FilterAssemblyName(args));

                  }));
            }
        }

        /// <summary>
        /// Check if end of application session should be canceled or not
        /// (we may have gotten here through unhandled exceptions - so we
        /// display it and attempt CONTINUE so user can save his data.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
        {
            base.OnSessionEnding(e);

            try
            {
                if (this.mMainWin != null && this.mOKToClose == true)
                {
                    if (this.mMainWin.DataContext != null)
                    {
                        e.Cancel = true;

                        ServiceResolver.Instance.MessageBoxService.ShowWarning(string.Format(CultureInfo.CurrentCulture,
                            "The application received a request to shutdown.\n\nPlease save your data and exit manually (click on the link below to report this problem if it persists)."),
                            "Attempt to exit");
                    }
                }
            }
            catch
            {
                
            }
        }

        /// <summary>
        /// Filter name of executeable if present in command line args
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private static List<string> FilterAssemblyName(IEnumerable<string> args)
        {
            List<string> filterCmdLineArgs = new List<string>();

            if (args != null)
            {
                int Cnt = 0;
                foreach (string s in args)
                {
                    Cnt++;

                    if (Cnt == 1)  // Always remove first command line parameter
                        continue;                // since this is the assembly entry name (CodeSHARPer.exe)

                    filterCmdLineArgs.Add(s);
                }
            }

            return filterCmdLineArgs;
        }

        /// <summary>
        /// This is the first bit of code being executed when the application is invoked (main entry point).
        /// 
        /// Use the <paramref name="e"/> parameter to evaluate command line options.
        /// Invoking a program with an associated file type extension (eg.: *.txt) in Explorer
        /// results, for example, in executing this function with the path and filename being
        /// supplied in <paramref name="e"/>.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            try
            {
                this.DispatcherUnhandledException += App_DispatcherUnhandledException;

                Workspace.Instance.LoadConfigOnAppStartup();
            }
            catch { }

            try
            {
                FrameworkElement.LanguageProperty.OverrideMetadata(
                    typeof(FrameworkElement),
                    new FrameworkPropertyMetadata(
                        XmlLanguage.GetLanguage(
                        CultureInfo.CurrentCulture.IetfLanguageTag)));

                Thread.CurrentThread.CurrentCulture = new CultureInfo("pt-PT");
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("pt-PT");
            }
            catch { }

            try
            {
                this.mOKToClose = true;
                this.mMainWin = new MainWindowView();
                this.mMainWin.FontSize = Workspace.Instance.Config.FontSize;

                App.CreateAppDataFolder();

                if (this.mMainWin != null)
                {
                    this.mMainWin.Closing += this.OnClosing;

                    // When the ViewModel asks to be closed, close the window.
                    // Source: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
                    Workspace.Instance.RequestClose += delegate
                    {
                        // Save session data and close application
                        this.OnClosed(this.mMainWin.DataContext as Workspace, this.mMainWin);

                        this.mOKToClose = true;
                    };

                    this.LoadSession(Workspace.Instance, this.mMainWin);
                    this.mMainWin.Show();
                    this.mOKToClose = true;

                    if (e != null)
                        ProcessCmdLine(e.Args);
                }
            }
            catch
            {
            }
        }

        private void LoadSession(Workspace workSpace, Window win)
        {
            try
            {
                win.DataContext = workSpace;

                // Establish command binding to accept user input via commanding framework
                workSpace.InitCommandBinding(win);

                MainWindowView mainWin = win as MainWindowView;

                string layoutFileName = System.IO.Path.Combine(App.DirAppData, Workspace.LayoutFileName);

                if (System.IO.File.Exists(layoutFileName) == true)
                {
                    var layoutSerializer = new XmlLayoutSerializer(mainWin.dockManager);

                    layoutSerializer.LayoutSerializationCallback += (s, e) =>
                    {
                        if (workSpace.Config.RememberLastOpenFiles == true)
                        {
                            if (!string.IsNullOrWhiteSpace(e.Model.ContentId) && System.IO.File.Exists(e.Model.ContentId))
                                Workspace.Instance.OpenSaftFile(e.Model.ContentId);
                        }
                    };

                    //layoutSerializer.Deserialize(layoutFileName);

                    //workSpace.SetActiveDocument(lastActiveFile);
                }
            }
            catch
            {
            }
        }

        /// <summary>
        /// Save session data on closing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            try
            {
                Workspace wsVM = Workspace.Instance;

                if (wsVM != null)
                {
                    if (wsVM.Exit_CheckConditions(sender) == true)      // Close all open files and check whether application is ready to close
                    {
                        wsVM.OnRequestClose();                          // (other than exception and error handling)

                        e.Cancel = false;
                    }
                    else
                        e.Cancel = wsVM.ShutDownInProgress_Cancel = true;
                }
            }
            catch 
            {
                
            }
        }

        /// <summary>
        /// Execute closing function and persist session data to be reloaded on next restart
        /// </summary>
        private void OnClosed(Workspace appVM, Window win)
        {
            try
            {
                // Save/initialize program options that determine global programm behaviour
                appVM.SaveConfigOnAppClosed();
            }
            catch (Exception exp)
            {
                //logger.Error(exp);
                ServiceResolver.Instance.MessageBoxService.ShowError(exp.Message, "Error in shut-down process");
            }
        }

        /// <summary>
        /// Handle unhandled exception here
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            string message = string.Empty;

            try
            {
                if (e.Exception != null)
                    message = string.Format(CultureInfo.CurrentCulture, "{0}\n\n{1}", e.Exception.Message, e.Exception.ToString());
                else
                    message = "An unknown error occurred.";

                ServiceResolver.Instance.MessageBoxService.ShowError(e.Exception.Message, "Unhandled Error");

                e.Handled = true;
            }
            catch 
            {
                
            }
        }
        #endregion methods
    }
}
